package com.tao.friend.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tao.common.creo.constants.CacheConstants;
import com.tao.common.creo.constants.HttpConstants;
import com.tao.common.creo.constants.Constants;
import com.tao.common.creo.domain.LoginUser;
import com.tao.common.creo.domain.Result;
import com.tao.common.creo.enums.ResultCode;
import com.tao.common.creo.enums.UserIdentity;
import com.tao.common.redis.service.RedisService;
import com.tao.common.security.exception.ServiceException;
import com.tao.common.security.service.TokenService;
import com.tao.friend.domain.user.User;
import com.tao.friend.domain.user.vo.LoginUserVO;
import com.tao.friend.domain.user.dto.UserDTO;
import com.tao.friend.mapper.UserMapper;
import com.tao.friend.service.IUserService;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements IUserService {


    @Autowired
    UserMapper userMapper;
    @Autowired
    RedisService redisService;

    @Autowired
    TokenService tokenService;

    @Value("${sms.code-expiration:5}")
    private Long phoneCodeExpiration; // 验证码过期时间, 单位分钟

    @Value("${sms.send-limit:3}")
    private Integer sendLimit; // 获取验证码次数限制

    @Value("${sms.is-send:false}")
    private boolean isSend;  //开关打开：true  开关关闭false

    @Value("${jwt.secret}")
    private String secret;
    // todo 待完成阿里云短信服务
    @Override
    public boolean sendCode(UserDTO userDTO) {
        // 校验手机号是否合法
        if(!checkPhone(userDTO.getPhone())) {
            throw new ServiceException(ResultCode.FAILED_PHONE);
        }
        // 获取redis 存储的key
        String phoneCodeKey = getPhoneCodeKey(userDTO.getPhone());
        // 判断是否频繁调用
        Long expiration = redisService.getExpire(phoneCodeKey, TimeUnit.SECONDS);
        if(expiration != null && (phoneCodeExpiration * 60 - expiration) < 60) {
            throw new ServiceException(ResultCode.FAILED_PHONE_CODE);
        }
        // 判断当前请求次数是否超过限制
        String createTimeKey = getCreateTimeKey(userDTO.getPhone());
        Long sendTimes = redisService.getCacheObject(createTimeKey, Long.class);
        if(sendTimes != null && sendTimes >= sendLimit) {
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }
        // 生成验证码, 然后存储到redis中, 然后返回给用户
        String code = isSend ? RandomUtil.randomNumbers(6) : Constants.DEFAULT_CODE;
        //存储到redis  数据结构：String  key：p:c:手机号  value :code
        redisService.setCacheObject(phoneCodeKey, code, phoneCodeExpiration, TimeUnit.MINUTES);
        // todo 判断是否调用阿里云短信服务

        // 发送成功. 统计获取验证码次数
        redisService.increment(createTimeKey);
        //  如果是当天第一次, 设置凌晨12点过期
        if(sendTimes == null) {
            Long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(createTimeKey, seconds, TimeUnit.SECONDS); // 设置过期时间
        }
        return true;
    }

    @Override
    public String codeLogin(String phone, String code) {
        checkCode(phone, code);
        // 根据手机号从数据库中获取用户
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        // 新用户, 开始注册用户
        if(user == null) {
            user = new User();
            user.setPhone(phone);
            user.setStatus(Constants.TRUE);
            userMapper.insert(user);
        }
        // 判断是否注册, 没注册就注册
        // 如果是老用户, 就直接登录, 并且将信息存储到缓存中
        return tokenService.creteToken(user.getUserId(), user.getNickName(), secret, UserIdentity.ORDINARY.getValue(), user.getHeadImage());
    }

    @Override
    public Result<LoginUserVO> info(String token) {
        if(StrUtil.isNotBlank(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, "");
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if(loginUser == null) {
            return Result.fail();
        }
        LoginUserVO loginUserDTO = new LoginUserVO();
        loginUserDTO.setNickName(loginUser.getNiceName());
        loginUserDTO.setHeadImage(loginUser.getHeadImage());
        return Result.success(loginUserDTO);
    }

    @Override
    public boolean logout(String token) {
        // 获取用户id
        Claims claims = tokenService.getClaims(token, secret);
        String userId = String.valueOf(tokenService.getUserId(claims));

        // 拼接key
        String redisKey = tokenService.getRedisKey(userId);
        // 删除redis中的信息
        return redisService.deleteObject(redisKey);
    }

    /**
     * 验证码是否正确
     */
    private void checkCode(String phone, String code) {
        String phoneCodeKey = getPhoneCodeKey(phone);
        // 判断是否已经获取验证码
        String cacheCode = redisService.getCacheObject(phoneCodeKey, String.class);
        if(StrUtil.isEmpty(cacheCode)) {
            throw new ServiceException(ResultCode.FAILED_NOT_GET_CODE);
        }
        // 判断验证码是否正确
        if(!cacheCode.equals(code)) {
            throw new ServiceException(ResultCode.FAILED_PHONE_CODE_ERROR);
        }
        // 将验证码从 redis 中删除
        redisService.deleteObject(phoneCodeKey);
    }

    private String getPhoneCodeKey(String phone) {
        return CacheConstants.PHONE_CODE_KEY + phone;
    }

    private String getCreateTimeKey(String phone) {
        return CacheConstants.PHONE_TIME_KEY + phone;
    }



    /**
     * 检查手机号是否合法
     * @param phone 手机号
     * @return true-合法，false-不合法
     */
    private boolean checkPhone(String phone) {
        if (phone == null || phone.trim().isEmpty()) {
            return false;
        }
        // 中国大陆手机号正则表达式
        String phoneRegex = "^1[3-9]\\d{9}$";
        return phone.matches(phoneRegex);
    }
}
